#include "a4print.h"

#include <string>
#include <stdio.h>

using namespace std;

namespace a4print
{

void test(void)
{
	return;
}

class Print
{
public:
	Print();
	~Print();

	int FindPrinter(int num);
	int SetOption(const std::string&option_name, const std::string& value);
	int ClearOption(void);
	int PrintFile(const std::string& fname, const std::string& title);

	int set_make_model(const std::string name);
	int set_print_num(int num);
	int set_print_uri(const std::string uri);
	int set_print_name(const std::string name);
	int set_print_code(int code);
	int set_print_status(int err);

	int print_num(void);
	int print_code(void);
	int print_status(void);
	std::string print_name(void);
	std::string make_model(void);
	std::string print_uri(void);

public:
	std::string print_make_ = "";

private:

	int print_num_ = 0;
	int print_status_ = 0;

	std::string print_name_ = "";
	std::string file_name_ = "";
	std::string make_model_ = "";
	std::string print_uri_ = "";

	int options_num_ = 0;
	cups_option_t *cups_options_ = NULL;

	std::list<std::string> list_;
};

Print::Print(){}
Print::~Print(){}

int Print::FindPrinter(int num)
{
	cups_dest_t *dests = NULL;
	int num_printer = cupsGetDests(&dests);
	if(!num_printer){
		return 0;
	}

	print_name_ = "";
	int i = num;
	cups_dest_t *dest = cupsGetDest(dests[i].name, NULL, num_printer, dests);
	if(!dest || !dest->options){
		goto out;
	}

	//printf("Find printer num is :%d, \n", i);

	for(int j = 0; j < dest->num_options; ++j) {

		std::string name = dest->options[j].name;
		std::string value = dest->options[j].value;
		//printf("name is : %s, value is: %s\n", name.c_str(), value.c_str());

		if(name == "printer-info"){
			print_name_ = value;
		}else if(name == "printer-state"){
			print_status_ = std::stoi(value);
		}else if(name == "printer-uri-supported"){
			print_uri_ = value;
		}else if(name == "printer-make-and-model"){
			print_make_ = value;
		}
	}

out:
	cupsFreeDests(num_printer, dests);

	return print_name_.length();
}

int Print::SetOption(const std::string& option_name, const std::string& value)
{
	list_.push_back(option_name);
	options_num_ = cupsAddOption((char *)option_name.data(), (char *)value.data(), options_num_, &cups_options_);

	return 0;
}

int Print::ClearOption(void)
{
	while(list_.size() > 0) {

		std::string name = list_.back();
		options_num_ = cupsRemoveOption(name.data(), options_num_, &cups_options_);
	}

	return 0;
}

int Print::PrintFile(const std::string& fname, const std::string& title)
{
	if(print_name_.empty()
	   || fname.empty()) {
		return -1;
	}

	int id = cupsPrintFile(print_name_.data(), fname.data(), title.data(), options_num_, cups_options_);

	ClearOption();

	return id;
}

int Print::set_make_model(const std::string name)
{
	make_model_ = name;
	return 0;
}

int Print::set_print_name(const std::string name)
{
	print_name_ = name;
	return 0;
}

int Print::set_print_uri(const std::string uri)
{
	print_uri_ = uri;
	return 0;
}

int Print::set_print_num(int num)
{
	print_num_ = num;
	return 0;
}

int Print::set_print_status(int err)
{
	print_status_ = err;
	return 0;
}

int Print::print_num(void)
{
	return print_num_;
}

int Print::print_status(void)
{
	return print_status_;
}

std::string Print::print_name(void)
{
	return print_name_;
}

std::string Print::make_model(void)
{
	return make_model_;
}

std::string Print::print_uri(void)
{
	return print_uri_;
}

Print a4;

int set_printer_num(int num)
{
	a4.set_print_num(num);

	return 0;
}

ipp_t *get_jobs(void)
{
  ipp_t *request = ippNewRequest(IPP_GET_JOBS);

  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
               NULL, a4.print_uri().c_str());
  ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
               NULL, cupsUser());

  return (cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/"));
}

int check_using_jobs(void)
{
	ipp_t *response = get_jobs();
	if(response != NULL){
		ipp_attribute_t *attr;
		const char *attrname;
		int job_id = 0;
		const char *job_name = NULL;
		const char *job_originating_user_name = NULL;

		//puts("Job ID  Owner             Title");
		//puts("------  ----------------  ---------------------------------");

		for (attr = ippFirstAttribute(response); attr != NULL; attr = ippNextAttribute(response)){
		/* Attributes without names are separators between jobs */
			attrname = ippGetName(attr);
			if (attrname == NULL){
				if (job_id > 0){
					if (job_name == NULL)
					job_name = "(withheld)";

					if (job_originating_user_name == NULL)
					job_originating_user_name = "(withheld)";

					//printf("%5d  %-16s  %s\n", job_id, job_originating_user_name, job_name);
					return 1;
				}

				job_id = 0;
				job_name = NULL;
				job_originating_user_name = NULL;
				continue;
			}
			else if (!strcmp(attrname, "job-id") && ippGetValueTag(attr) == IPP_TAG_INTEGER)
			job_id = ippGetInteger(attr, 0);
			else if (!strcmp(attrname, "job-name") && ippGetValueTag(attr) == IPP_TAG_NAME)
			job_name = ippGetString(attr, 0, NULL);
			else if (!strcmp(attrname, "job-originating-user-name") &&
					ippGetValueTag(attr) == IPP_TAG_NAME)
			job_originating_user_name = ippGetString(attr, 0, NULL);
		}

		if (job_id > 0)	{
			if (job_name == NULL)
			job_name = "(withheld)";

			if (job_originating_user_name == NULL)
			job_originating_user_name = "(withheld)";
			//printf("%5d  %-16s  %s\n", job_id, job_originating_user_name, job_name);
			return 1;
		}
	}

	return 0;
}

int GetStatus(void)
{
	//const char *name; /* printer name */
	//http_t *http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
	ipp_t *request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
	//char printer_uri[1024];
	ipp_t *response;
	ipp_attribute_t *printer_state;
	ipp_attribute_t *printer_state_message;
	ipp_attribute_t *printer_state_reasons;

	// httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
	// "ipp", NULL, cupsServer(), ippPort(),
	// "/printers/%s", name);

	//printf("printer uri is : %s, name is: %s\n", printer_uri, name);

	// ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
	// "printer-uri", NULL, printer_uri);

	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
	"printer-uri", NULL, a4.print_uri().c_str());

	//response = cupsDoRequest(http, request, "/");
	response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
	if(!response){
		printf("Response is NULL\n");
		return 2;
	}

	// printer_state = ippFindAttribute(response,
	// "printer-state",
	// IPP_TAG_TEXT);
	// printer_state_message = ippFindAttribute(response,
	// "printer-state-message",
	// IPP_TAG_TEXT);
	printer_state_reasons = ippFindAttribute(response,
	"printer-state-reasons",
	IPP_TAG_KEYWORD);

	// /* do something wih message and reasons */
	std::string state = (char *)ippGetString(printer_state_reasons, 0, NULL);
	ippDelete(response);
	//httpClose(http);

	//printf("printer_state_reasons is : %s\n", state.c_str());
	if(state.compare("none") == 0 ){
		return 0;
	}else{
		int is_used = check_using_jobs();
		if(is_used){
			return 1;
		}
	}

	return 2;
}

int GetName(void)
{
	ipp_t *request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);

	ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
				NULL, a4.print_uri().c_str());
	ipp_t *response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
	for(ipp_attribute_t *attr = ippFirstAttribute(response); attr != NULL; attr = ippNextAttribute(response)){
		std::string attrname = (char *)ippGetName(attr);
		if(attrname == "printer-name"){
			const char *value = ippGetString(attr, 0, NULL);
			a4.set_print_name(value);
			//printf("set printer name is : %s\n", value);
		}else if(attrname == "printer-make-and-model,"){
			const char *value = ippGetString(attr, 0, NULL);
			a4.print_make_ = value;
		}
		// if(ippGetValueTag(attr) == IPP_TAG_INTEGER){
		// 	int value = ippGetInteger(attr, 0);
		// 	//printf("attrname is : %s, value is d : %d\n", attrname.c_str(), value);
		// }else{
		// 	const char *value = ippGetString(attr, 0, NULL);
		// 	//printf("attrname is : %s, value is s : %s\n", attrname.c_str(), value);
		// }
	}
	return 0;
}

int init(void)
{
	int len = a4.FindPrinter(a4.print_num());
	if(!len) {
		return -1;
	}

	int err = GetStatus();
	a4.set_print_status(err);

	GetName();

	return 0;
}

int close(void)
{
	return 0;
}

char *getInfo(void)
{
	std::string info;
	info.append("{\n");
	info.append("\"type\":\"1050\",\n");
	info.append("\"name\":\"A4Printer\",\n");
	info.append("\"manufacturers\"").append(":").append("\"").append(a4.print_make_).append("\"").append(",\n");
	info.append("\"model\"").append(":").append("\"").append(a4.print_make_).append("\"").append(",\n");
	info.append("\"sdk\":\"1.0.0\",\n");
	info.append("\"description\":\"\"\n");
	info.append("}\n");

	char *data = (char *)malloc(info.length());
	strncpy(data, info.data(), info.length());

	return data;
}

int getStatus(void)
{
	// int err = init();
	// if(err) {
	// 	return 2;
	// }

	int err = GetStatus();
	a4.set_print_status(err);

	return err;
}

int printA4(char* full_path, int score = 0, int direction = 0, int percent = 0, int left = 0, int right = 0, int top = 0, int bottom = 0)
{
	if(!full_path){
		return -1;
	}

	if(score) {
		///a4.SetOption("score", std::to_string(score));
	}
	if(direction){
		///a4.SetOption("direction", std::to_string(direction));
	}
	if(direction){
		///a4.SetOption("percent", std::to_string(percent));
	}
	if(left){
		///a4.SetOption("left", std::to_string(left));
	}
	if(right){
		///a4.SetOption("right", std::to_string(right));
	}
	if(top){
		///a4.SetOption("top", std::to_string(top));
	}
	if(bottom){
		///a4.SetOption("bottom", std::to_string(bottom));
	}

	return a4.PrintFile(full_path, "");
}

} // namespace a4print
